package naruto.common;

import gov.nasa.jpf.jvm.bytecode.*;
import java.util.*;

/**
 *
 * @author nacuong
 */
public class InstructionCommon {
  public static boolean isLibraryInstruction(Instruction insn) {
    String methodInfo = insn.getMethodInfo().getCompleteName();
    return  methodInfo.startsWith("java.") 	||
						methodInfo.startsWith("javax.") ||
						methodInfo.startsWith("sun.") 	||
						methodInfo.startsWith("com.sun.")
				;
  }

  public static boolean isInSameMethod(Instruction insn1, Instruction insn2, Hashtable<String, Integer> recursiveCounter){
    String methodInfoName1 = insn1.getMethodInfo().getCompleteName();
    String methodInfoName2 = insn2.getMethodInfo().getCompleteName();

    return recursiveCounter.get(methodInfoName1) == recursiveCounter.get(methodInfoName2)
            && methodInfoName1.equals(methodInfoName2);
  }

  public static boolean isMethodCall(Instruction insn){
    return insn instanceof InvokeInstruction;
  }

  public static boolean isIfInstruction(Instruction insn){
    return insn instanceof IfInstruction;
  }

  public static boolean isLoopInstruction(IfInstruction insn){
    int jumpOffset = ((IfInstruction) insn).getTarget().getOffset();

    Instruction next = insn;

    while (next.getOffset() < jumpOffset){
      next = next.getNext();
      if (next instanceof GOTO) {
        int gotoOffset = ((GOTO) next).getTarget().getOffset();
        if (gotoOffset <= next.getOffset()) return true;
      }
    }

    return false;
  }

  public static boolean isEndIfInstruction(Instruction insn, IfInstruction ifInsn, Hashtable<String, Integer> recursiveCounter){
    boolean reachEndIf = insn.getOffset() == getEndIfPosition(ifInsn);
    /* I'm not sure whether I need this reachreturn boolean */
    boolean reachReturn = insn instanceof ReturnInstruction && isInSameMethod(insn, ifInsn, recursiveCounter);

    return reachEndIf  || reachReturn;
  }

  public static int getEndIfPosition(IfInstruction ifInsn){
    int jumpOffset = ifInsn.getTarget().getOffset();
    /* endif instruction is where the for jumps to when false */
    if (isLoopInstruction(ifInsn))
      return jumpOffset;
    else {
    /* endif instruction is where the goto that appears right before the jump instruction goes to */
      Instruction jumpInstruction = ifInsn.getNext();
      while (jumpInstruction.getOffset() < jumpOffset)
        jumpInstruction = jumpInstruction.getNext();

      Instruction previousJump = jumpInstruction.getPrev();
      /* this is the case of if-else branch */
      if (previousJump instanceof GOTO)
        return ((GOTO)previousJump).getTarget().getOffset();
      /* this is the case of single if */
      else return jumpOffset;
    }
  }
}
